//
// string.c
// internals of utf-8 and byte strings
//
// (c) 2008 why the lucky stiff, the freelance professor
//
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "potion.h"
#include "internal.h"
#include "khash.h"
#include "table.h"

// TODO hsq 为什么要多分配？以及为什么是这种形式？
#define BYTES_FACTOR 1 / 8 * 9
#define BYTES_CHUNK  32
// 取得 len 字节有效荷载的 PNBytes 整体对齐（整数倍）到 BYTES_CHUNK 后有效荷载。
#define BYTES_ALIGN(len) PN_ALIGN(len + sizeof(struct PNBytes), BYTES_CHUNK) - sizeof(struct PNBytes)

// 检查 PNBYTES 容量，按需扩容。
#define CHECK_BYTES_SIZE(buf, len) \
  if ((buf)->len + (len) + 1 > (buf)->siz) { \
    size_t siz = BYTES_ALIGN((((buf)->len + (len)) * BYTES_FACTOR) + 1); \
    PN_REALLOC((buf), PN_TBYTES, struct PNBytes, siz); \
    (buf)->siz = (PN_SIZE)siz; \
  }

// 将 PNString s 加入 VM 字符串池。
void potion_add_str(Potion *P, PN s) {
  int ret;
  // TODO hsq kh_put 的几处调用后都没用到 ret ？
  kh_put(str, P->strings, s, &ret);
  PN_QUICK_FWD(struct PNTable *, P->strings);
}

// 获取 str 在字符串池中的唯一副本指针，或 PN_NIL 。
static PN potion_lookup_str(Potion *P, const char *str, size_t len) {
  vPN(Table) t = P->strings;
  unsigned k = kh_get(str, t, ((kh_str_key){.ptr = str, .len = len}));
  if (k != kh_end(t)) return kh_key(str, t, k);
  return PN_NIL;
}

// 获取 str 开始的 len 字节在字符串池中的唯一副本指针，按需加入。
PN potion_str2(Potion *P, const char *str, size_t len) {
  PN exist = potion_lookup_str(P, str, len);
  if (exist == PN_NIL) {
    vPN(String) s = PN_ALLOC_N(PN_TSTRING, struct PNString, len + 1);
    s->len = (PN_SIZE)len;
    PN_MEMCPY_N(s->chars, str, char, len);
    s->chars[len] = '\0';

    potion_add_str(P, (PN)s);
    exist = (PN)s;
  }
  return exist;
}

// 类似 snprintf ，依照格式串 format 构造字符串对象 PNString 并返回。
PN potion_str_format(Potion *P, const char *format, ...) {
  vPN(String) s;
  PN_SIZE len;
  va_list args;

  va_start(args, format);
  len = (PN_SIZE)vsnprintf(NULL, 0, format, args);
  va_end(args);
  s = PN_ALLOC_N(PN_TSTRING, struct PNString, len + 1);

  va_start(args, format);
  vsnprintf(s->chars, len + 1, format, args);
  va_end(args);

  return (PN)s;
}

// 依照格式串 self 构造字符串对象 PNString 并返回。
//    {} 为插入占位符，#{} 为其转义。
PN_MSG(potion_str_format_msg, ...) {
  #define VAR "{}"
  #define LEN (sizeof VAR - 1)
  #define ESC '#'

  va_list args;
  const char *sep, *format = PN_STR_PTR(self);
  PN bytes = potion_bytes_str(P, "");
  vPN(Bytes) buf = (struct PNBytes *)bytes;
  size_t is_esc, len;

  va_start(args, self);
  while (*format) {
    sep = strstr(format, VAR);
    is_esc = (format < sep && sep[-1] == ESC);
    len = sep ? sep - is_esc - format : strlen(format);
    if (len > 0) {
      CHECK_BYTES_SIZE(buf, len);
      PN_MEMCPY_N(buf->chars + buf->len, format, char, len);
      buf->len += len;
      buf->chars[buf->len] = '\0';
    }
    if (NULL == sep) break;
    if (is_esc) {
      POTION_BYTES_LITERAL(bytes, VAR);
    } else { // NOTE 即使参数数量不足，也要全部处理，否则转义会被多次替换。
      potion_bytes_obj_string(P, bytes, va_arg(args, PN));
    }
    format = sep + LEN;
  }
  va_end(args);

  return PN_STR_B(bytes);
  #undef VAR
  #undef LEN
  #undef ESC
}

// 取 utf8 字符数
PN_MSG(potion_str_length) {
  return PN_NUM(potion_cp_strlen_utf8(PN_STR_PTR(self)));
}

// 将 PNString/PNBytes self 作为源码执行。
PN_MSG(potion_str_eval) {
  return potion_eval(P, self);
}

// 解析 PNString/PNBytes self --> PNSource 。
PN_MSG(potion_str_parse) {
  // return potion_parse(P, potion_bytes_str(P, PN_STR_PTR(self)));
  return potion_parse(P, self);
}

// 执行 shell 命令 PNString/PNBytes self --> 退出码 。
// TODO hsq 封装 popen/pclose ？
PN_MSG(potion_str_shell) {
  return PN_NUM(system(PN_STR_PTR(self)));
}

// 字符串 self 转 Number ；
// 当前是手工代码转十进制整数，且 <INT_MAX ； strtod 转浮点数（支持十六进制）。
// TODO hsq 使用 atoi strtol ？支持多进制？
PN_MSG(potion_str_number) {
  char *str = PN_STR_PTR(self);
  int i = 0, dec = 0, sign = 0, len = PN_STR_LEN(self);
  if (len < 1) return PN_ZERO;

  sign = (str[0] == '-' ? -1 : 1);
  if (str[0] == '-' || str[0] == '+') {
    dec++; str++; len--;
  }
  for (i = 0; i < len; i++)
    if (str[i] < '0' || str[i] > '9')
      break;
  if (i < 10 && i == len) {
    return PN_NUM(sign * PN_ATOI(str, i, 10));
  }

  return potion_decimal(P, PN_STR_PTR(self), PN_STR_LEN(self));
}

// 返回自身 PNString self 。
PN_MSG(potion_str_string) {
  return self;
}

// 在标准输出打印 utf8 字符串 PNString self ，返回 nil 。
PN_MSG(potion_str_print) {
  printf("%s", PN_STR_PTR(self));
  return PN_NIL;
}

// 将 utf8 串 str 的字符索引 index 转化为字节索引。
static size_t potion_utf8char_offset(const char *s, size_t index) {
  int i;
  for (i = 0; s[i]; i++)
    if ((s[i] & 0xC0) != 0x80)
      if (index-- == 0)
        return i;
  return i;
}

// 规范化字符串索引： nil -> nilvalue, <0 -> >=0(之后 <0 -> 0), >len -> len
//  len 是 utf8 字符数，即末字符后一位置。
inline static int potion_str_slice_index(PN index, size_t len, int nilvalue) {
  int i = PN_INT(index);
  int corrected;
  if (PN_IS_NIL(index)) {
    corrected = nilvalue;
  } else if (i < 0) {
    corrected = i + len;
    if (corrected < 0) {
      corrected = 0;
    }
  } else if ((size_t)i > len) {
    corrected = len;
  } else {
    corrected = i;
  }
  return corrected;
}

// 取 utf8 串 self 的 slice ，不在范围内则返回空串；
//  start 缺省为 0 ， end 缺省为字符数（即末字符后一位置）； end<start 则 end+=start 。
// TODO hsq 'ab' slice (1) 报错 Bus error: 10 ，但 end!=PN_NIL 所以无法给出缺省值。
PN_MSG(potion_str_slice, PN start, PN end) {
  char *str = PN_STR_PTR(self);
  size_t len = potion_cp_strlen_utf8(str);
  size_t startoffset = potion_utf8char_offset(str, potion_str_slice_index(start, len, 0));
  /* patch applied by http://github.com/citizen428 */
  if (end < start) end += start;
  size_t endoffset = potion_utf8char_offset(str, potion_str_slice_index(end, len, len));
  /* end patch */
  return potion_str2(P, str + startoffset, endoffset - startoffset);
}

// 取 utf8 串 self 的 index 处的字符（串），不在范围内则返回空串。
PN_MSG(potion_str_at, PN index) {
  return potion_str_slice(P, closure, self, index, PN_NUM(PN_INT(index) + 1));
}

// 创建 str 的 PNBytes 结构副本。
PN potion_bytes_str(Potion *P, const char *str) {
  size_t len = strlen(str);
  vPN(Bytes) s = (struct PNBytes *)potion_bytes(P, len);
  PN_MEMCPY_N(s->chars, str, char, len + 1);
  // s->chars[len] = '\0';
  return (PN)s;
}

// 创建可容纳 len 字节的结构 PNBytes ， 分配的内存为 BYTES_CHUNK 整数倍。
PN potion_bytes(Potion *P, size_t len) {
  size_t siz = BYTES_ALIGN(len + 1);
  vPN(Bytes) s = PN_ALLOC_N(PN_TBYTES, struct PNBytes, siz);
  s->siz = (PN_SIZE)siz;
  s->len = (PN_SIZE)len;
  return (PN)s;
}

// 类似 snprintf ，依照格式串 format 构造字符串附加到 PNBytes 中，返回新增字符数。
PN_SIZE potion_bytes_format(Potion *P, PN bytes, const char *format, ...) {
  PN_SIZE len;
  va_list args;
  vPN(Bytes) s = (struct PNBytes *)potion_fwd(bytes);

  va_start(args, format);
  len = (PN_SIZE)vsnprintf(NULL, 0, format, args);
  va_end(args);

  CHECK_BYTES_SIZE(s, len);

  va_start(args, format);
  vsnprintf(s->chars + s->len, len + 1, format, args);
  va_end(args);

  s->len += len;
  return len;
}

// 将字符串字面量 text 附加到 PNBytes 中，返回新增字符数。
PN_SIZE potion_bytes_literal(Potion *P, PN bytes, const char *text, PN_SIZE len) {
  vPN(Bytes) s = (struct PNBytes *)potion_fwd(bytes);

  CHECK_BYTES_SIZE(s, len);

  PN_MEMCPY_N(s->chars + s->len, text, char, len + 1);
  s->len += len;
  // s->chars[s->len] = '\0';
  return len;
}


PN_MSG(potion_bytes_append, PN str);

// 将 obj 字符串化（发送 string 消息）后附加到 PNbytes bytes 中
void potion_bytes_obj_string(Potion *P, PN bytes, PN obj) {
  potion_bytes_append(P, 0, bytes, potion_send(obj, $(string)));
}

// 将 PNString str 附加到 PNBytes self 中。
PN_MSG(potion_bytes_append, PN str) {
  vPN(Bytes) s = (struct PNBytes *)potion_fwd(self);
  PN fstr = potion_fwd(str);
  PN_SIZE len = PN_STR_LEN(fstr);

  CHECK_BYTES_SIZE(s, len);

  PN_MEMCPY_N(s->chars + s->len, PN_STR_PTR(fstr), char, len + 1);
  s->len += len;
  // s->chars[s->len] = '\0';
  return self;
}

// 取字节串 PNBytes 中的字节数。
// TODO hsq 似乎只有 bytes/tuple/table 需要处理 fwd 。
PN_MSG(potion_bytes_length) {
  PN str = potion_fwd(self);
  return PN_NUM(PN_STR_LEN(str));
}

// 获取 PNBytes self 在字符串池中的唯一副本指针，按需加入。
// TODO: ensure it's UTF-8 data
PN_MSG_G(potion_bytes_string) {
  self = potion_fwd(self);
  PN exist = potion_lookup_str(P, PN_STR_PTR(self), PN_STR_LEN(self));
  if (exist == PN_NIL) {
    PN_SIZE len = PN_STR_LEN(self);
    vPN(String) s = PN_ALLOC_N(PN_TSTRING, struct PNString, len + 1);
    s->len = len;
    PN_MEMCPY_N(s->chars, PN_STR_PTR(self), char, len + 1);
    potion_add_str(P, (PN)s);
    exist = (PN)s;
  }
  return exist;
}

// 在标准输出打印字节串 PNBytes self ，返回 nil 。
PN_MSG(potion_bytes_print) {
  PN str = potion_fwd(self);
  printf("%s", PN_STR_PTR(str));
  return PN_NIL;
}

// 列举字符串池，收集到 tuple 并返回。
PN_MSG(potion_strings) {
  PN ss = PN_TUP0();
  KH_EACH_ASC(str, P->strings, {
    PN_PUSH(ss, kh_key(str, t, k));
  });
  return ss;
}
// TODO hsq VM 启动后（或者脚本解析后）列出池中所有字符串，检查是否有必要入池。
//    potion_str_hash_init 只在启动 VM 时调用，可收集或估计实际字符串数量？
// TODO hsq 有必要所有字符串都收入 VM 池中？搜索 PN_LITERAL PN_STRING potion_str2 和
//    PN_STR_B(potion_bytes_string 用在了 bytes 的 string 消息) join

// 初始化 VM 的字符串表为空表。
void potion_str_hash_init(Potion *P) {
  P->strings = PN_CALLOC(PN_TSTRINGS, struct PNTable);
}

// 注册虚表 PNString 和 PNBytes 的消息，注册虚表 PNString 的 .call 为取某位置的字符。
void potion_str_init(Potion *P) {
  PN str_vt = PN_VTABLE(PN_TSTRING);
  PN byt_vt = PN_VTABLE(PN_TBYTES);
  potion_type_call_is(str_vt, PN_FUNC(potion_str_at,    "index=N"));
  PN_METHOD(str_vt,   "eval",    potion_str_eval,       0);
  PN_METHOD(str_vt,   "parse",   potion_str_parse,      0);
  PN_METHOD(str_vt,   "shell",   potion_str_shell,      0);
  PN_METHOD(str_vt,   "length",  potion_str_length,     0);
  PN_METHOD(str_vt,   "number",  potion_str_number,     0);
  PN_METHOD(str_vt,   "print",   potion_str_print,      0);
  PN_METHOD(str_vt,   "string",  potion_str_string,     0);
  PN_METHOD(str_vt,   "slice",   potion_str_slice,      "start=N,end=N");
  PN_METHOD(str_vt,   "format",  potion_str_format_msg, "tpl=S");
  PN_METHOD(byt_vt,   "append",  potion_bytes_append,   "str=S");
  PN_METHOD(byt_vt,   "length",  potion_bytes_length,   0);
  PN_METHOD(byt_vt,   "print",   potion_bytes_print,    0);
  PN_METHOD(byt_vt,   "string",  potion_bytes_string,   0);
  PN_METHOD(P->lobby, "strings", potion_strings,        0);
}
